﻿using System;
using GammaJul.ReSharper.GammaXaml.Psi;
using GammaJul.ReSharper.GammaXaml.Psi.Xaml;
using JetBrains.Annotations;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Intentions.Extensibility;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Xaml.Impl;
using JetBrains.ReSharper.Psi.Xaml.Impl.Util;
using JetBrains.ReSharper.Psi.Xaml.Tree;
using JetBrains.ReSharper.Psi.Xaml.Tree.MarkupExtensions;
using JetBrains.ReSharper.Psi.Xml.Parsing;
using JetBrains.ReSharper.Psi.Xml.Tree;
using JetBrains.TextControl;
using JetBrains.Util;

namespace GammaJul.ReSharper.GammaXaml.ContextActions.Xaml {

	/// <summary>
	/// A context action that transforms a XAML <c>{RelativeSource}</c> markup extension into a <c>{x:Static}</c> one
	/// to reduce memory usage by reusing the same object.
	/// </summary>
	/// <remarks>Only available for WPF since Silverlight doesn't provide those static properties.</remarks>
	public abstract class RelativeSourceToXStaticActionBase : BulbActionBase {

		/// <summary>
		/// Finds the <see cref="IRelativeSourceUsage"/> that this action uses.
		/// </summary>
		/// <returns>A <see cref="IRelativeSourceUsage"/> implementation, or <c>null</c>.</returns>
		[CanBeNull]
		protected abstract IRelativeSourceUsage GetRelativeSource();

		[NotNull]
		private static string GetXStaticMarkupString(RelativeSourceMode mode) {
			return "{x:Static RelativeSource." + mode.ToKeyword() + "}";
		}

		[NotNull]
		private static string GetXStaticTagString(RelativeSourceMode mode) {
			return "<x:Static Member=\"RelativeSource." + mode.ToKeyword() + "\"/>";
		}

		public override string Text {
			get {
				var relSource = GetRelativeSource();
				Assertion.AssertNotNull(relSource, "relSource != null");
				string xStaticString = relSource is IMarkup ? GetXStaticMarkupString(relSource.Mode) : GetXStaticTagString(relSource.Mode);
				return "Use '" + xStaticString + "'";
			}
		}

		protected override Action<ITextControl> ExecutePsiTransaction(ISolution solution, IProgressIndicator progress) {
			var relSource = GetRelativeSource();
			if (relSource == null)
				return null;

			XamlElementFactory elementFactory = XamlElementFactory.GetElementFactory(relSource);

			ITreeNode xStaticNode;
			if (relSource is IMarkup)
				xStaticNode = elementFactory.CreateMarkup(GetXStaticMarkupString(relSource.Mode));
			else if (relSource is IXmlTag)
				xStaticNode = elementFactory.CreateTag(GetXStaticTagString(relSource.Mode), relSource.GetPsiServices(), relSource.GetPsiModule(), true);
			else
				xStaticNode = null;

			if (xStaticNode != null)
				relSource.ReplaceBy(xStaticNode);

			return null;
		}

		/// <summary>
		/// Gets whether this action is available on a given <see cref="IRelativeSourceUsage"/>.
		/// </summary>
		/// <param name="relativeSource">The relative source.</param>
		/// <returns><c>true</c> if <c>x:Static</c> can be used instead of <paramref name="relativeSource"/>, <c>false</c> otherwise.</returns>
		public static bool IsAvailable([CanBeNull] IRelativeSourceUsage relativeSource) {
			if (relativeSource == null || !(relativeSource is IMarkup || relativeSource is IXmlTag))
				return false;

			// this action only has a meaning on TemplatedParent, Self and PreviousData
			RelativeSourceMode mode = relativeSource.Mode;
			if (mode != RelativeSourceMode.TEMPLATED_PARENT && mode != RelativeSourceMode.SELF && mode != RelativeSourceMode.PREVIOUS_DATA)
				return false;

			// and only in WPF since Silverlight doesn't have the corresponding static properties
			return SilverlightUtil.GetSilverlightness(relativeSource.GetPsiModule()) == Silverlightness.NO_SILVERLIGHT;
		}

	}

}